﻿using MiniFox.Components;
using MiniFox.Platform.Exceptions;
using MiniFox.Platform.File.Configurations;
using MiniFox.Platform.File.Models;
using MiniFox.Platform.File.Providers;

namespace MiniFox.Platform.File.Services
{
    /// <summary>
    /// 文件存储
    /// </summary>
    public abstract class StorageService : FileSystemBase, IStorageService
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="group"></param>
        protected StorageService(string group) : base(group)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        [AutoWired]
        protected IStorageProvider Provider { get; set; }

        #region delete
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileIds"></param>
        public virtual void Remove(params string[] fileIds)
        {
            Parallel.ForEach(fileIds, fileId =>
            {
                FileData fileData = this.Provider.GetFile(fileId);
                OnRemoving(fileData);
                if (!Provider.Remove(fileId))
                    throw new PlatformException("ERR_OperateFail");
                OnRemoved(fileData);
            });
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        protected virtual void OnRemoving(FileData fileData)
        {
            if (Cache.Contains(fileData.FileId))
            {
                Cache.Remove(fileData.FileId);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        protected abstract void OnRemoved(FileData fileData);

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        protected virtual void OnSaving(FileData fileData)
        {
        }

        #region sync
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileIds"></param>
        public virtual void Accept(string[] fileIds)
        {
            List<FileData> files = [];
            Parallel.ForEach(fileIds, (fileId) =>
            {
                var fileData = GetFileDatasFromCache(fileId);
                if (fileData == null)
                    fileData = this.Provider.LoadPreSave(fileId);
                if (fileData == null) return;

                fileData.UpdateTime = DateTime.Now;
                if (!Provider.Accept(fileData))
                    throw new PlatformException("ERR_OperateFail");
                OnSaved(fileData);
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileDatas"></param>
        protected virtual void PreSave(FileData[] fileDatas)
        {
            foreach (FileData fileData in fileDatas)
            {
                if (fileData == null) continue;
                SetFileDataToCache(fileData);// 进缓存
            }
            if (!Provider.PreSave(fileDatas))
                throw new PlatformException("ERR_OperateFail");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileDatas"></param>
        protected virtual void Save(FileData[] fileDatas)
        {
            Parallel.ForEach(fileDatas, (fileData) =>
            {
                if (fileData == null) return;
                if (!Provider.Save(fileData))
                    throw new PlatformException("ERR_OperateFail");
                OnSaved(fileData);
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="fileDatas"></param>
        /// <returns></returns>
        public IEnumerable<FileData> Upload(string path, FileData[] fileDatas)
        {
            Parallel.ForEach(fileDatas, (fileData) =>
            {
                fileData.Group = Group;
                fileData.Path = path;
                fileData.CreateTime = DateTime.Now;
                fileData.UpdateTime = DateTime.Now;
                OnSaving(fileData);
            });
            if (FileSetting.PreUpload)
            {
                PreSave(fileDatas);
            }
            else
            {
                Save(fileDatas);
            }
            return fileDatas;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        protected abstract void OnSaved(FileData fileData);
        #endregion

        #region async
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileIds"></param>
        /// <returns></returns>
        public virtual Task AcceptAsync(string[] fileIds)
        {
            return Parallel.ForEachAsync(fileIds, async (fileId, cancelToken) =>
            {
                var fileData = GetFileDatasFromCache(fileId);
                fileData.UpdateTime = DateTime.Now;
                if (!Provider.Accept(fileData))
                    throw new PlatformException("ERR_OperateFail");
                await OnSavedAsync(fileData);
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileDatas"></param>
        /// <returns></returns>
        protected virtual Task SaveAsync(FileData[] fileDatas)
        {
            return Parallel.ForEachAsync(fileDatas, async (fileData, cancelToken) =>
            {
                if (fileData == null) return;
                if (!Provider.Save(fileData))
                    throw new PlatformException("ERR_OperateFail");
                await OnSavedAsync(fileData);
            });
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="fileDatas"></param>
        /// <returns></returns>
        public async Task<IEnumerable<FileData>> UploadAsync(string path, FileData[] fileDatas)
        {
            Parallel.ForEach(fileDatas, (fileData) =>
            {
                fileData.Group = Group;
                fileData.Path = path;
                fileData.CreateTime = DateTime.Now;
                fileData.UpdateTime = DateTime.Now;
                OnSaving(fileData);
            });
            if (FileSetting.PreUpload)
            {
                PreSave(fileDatas);
            }
            else
            {
                await SaveAsync(fileDatas);
            }
            return fileDatas;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        protected abstract Task OnSavedAsync(FileData fileData);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileId"></param>
        /// <param name="newName"></param>
        /// <returns></returns>
        public void Rename(string fileId, string newName)
        {
            newName = newName.Trim('/');
            if (!Provider.Rename(this.Group, fileId, newName))
                throw new PlatformException("ERR_OperateFail");
        }

        #endregion
    }
}
